package com.common.base.widget

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.text.TextUtils
import android.util.AttributeSet
import android.view.View
import com.common.base.R

/**
 *
 * 作者：sosou
 *
 * 版本：1.0
 *
 * 创建日期：15/2/2023
 *
 * 描述：
 *
 * 修订历史：
 *
 */
class NodeProgressBar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) :
    View(context, attrs, defStyleAttr) {
    // 自定义View宽度
    private var mWidth = 0

    // 自定义View高度
    private var mHeight = 0

    // 节点个数
    private var mNodeCount: Int

    // 节点宽度
    private val mNodeWidth: Int

    // 节点高度
    private val mNodeHeight: Int

    // 未到达节点的资源id
    private val mNodeUnreached: Int

    // 已经到达节点的资源id
    private val mNodeReached: Int

    // 已完成节点的资源id
    private val mNodeFinished: Int

    // 失败节点的资源id
    private val mNodeFailed: Int

    // 节点大小比例，用于处理成功/失败节点比到达/未到达节点大的情况
    private val mNodeRatio: Float

    // 上方文字是否生效
    private var mTopTxtEnable: Boolean

    // 上方文字大小
    private val mTopTxtSize: Int

    // 上方文字颜色
    private val mTopTxtColor: Int

    // 上方文字距离节点的距离
    private val mTopTxtGap: Int

    // 上方文字的样式
    private val mTopTxtStyle: Int

    // 下方文字是否生效
    private var mBottomTxtEnable: Boolean

    // 下方文字的大小
    private val mBottomTxtSize: Int

    // 下方文字的颜色
    private val mBottomTxtColor: Int

    // 下方文字距离节点的距离
    private val mBottomTxtGap: Int

    // 下方文字的样式
    private val mBottomTxtStyle: Int

    // 下方提示文字的颜色（失败的节点）
    private val mBottomWarnTxtColor: Int

    // 相仿提示文字的样式（失败的节点使用）
    private val mBottomWarnTxtStyle: Int

    // 连接线的宽度
    private val mLineWidth: Int

    // 已到达的连接线颜色
    private val mReachedLineColor: Int

    // 未到达的连接线的颜色
    private val mUnreachedLineColor: Int

    // 节点区域横向宽度
    private val mRegionWidth: Int

    // 上方文字画笔
    private var mPaintTopTxt: Paint? = null

    // 底部文字画笔
    private var mPaintBottomTxt: Paint? = null

    // 底部提示文字的画笔
    private var mPaintBottomWarnTxt: Paint? = null

    // 节点画笔
    private var mPaintNode: Paint? = null

    // 未到达连线画笔
    private var mPaintUnreachedLine: Paint? = null

    // 已到达连线画笔
    private var mPaintReachedLine: Paint? = null

    // 未到达节点Bitmap
    private var mNodeUnreachedBitmap: Bitmap? = null

    // 已到达节点Bitmap
    private var mNodeReachedBitmap: Bitmap? = null

    // 失败节点Bitmap
    private var mNodeFailedBitmap: Bitmap? = null

    // 已完成节点Bitmap
    private var mNodeFinishedBitmap: Bitmap? = null

    // 上方文字的中心坐标列表
    private var mTopTxtLocationList: MutableList<Location>? = null

    // 中间节点的中心文字坐标列表
    private var mNodeLocationList: MutableList<Location>? = null

    // 下方文字的中心坐标列表
    private var mBottomTxtLocationList: MutableList<Location>? = null
    private var mNodeList: List<Node> = ArrayList()

    init {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.NodeProgressBar)
        mNodeCount = ta.getInt(R.styleable.NodeProgressBar_nodeCount, 0)
        mNodeWidth = ta.getDimensionPixelSize(R.styleable.NodeProgressBar_nodeWidth, 0)
        mNodeHeight = ta.getDimensionPixelSize(R.styleable.NodeProgressBar_nodeHeight, 0)
        mNodeUnreached =
            ta.getResourceId(R.styleable.NodeProgressBar_nodeUnreached, R.drawable.node_unreached)
        mNodeReached =
            ta.getResourceId(R.styleable.NodeProgressBar_nodeReached, R.drawable.node_unreached)
        mNodeFinished =
            ta.getResourceId(R.styleable.NodeProgressBar_nodeFinished, R.drawable.node_unreached)
        mNodeFailed =
            ta.getResourceId(R.styleable.NodeProgressBar_nodeFailed, R.drawable.node_unreached)
        mNodeRatio = ta.getFloat(R.styleable.NodeProgressBar_nodeRatio, 1.0f)
        mTopTxtEnable = ta.getBoolean(R.styleable.NodeProgressBar_topTxtEnable, false)
        mTopTxtSize = ta.getDimensionPixelSize(R.styleable.NodeProgressBar_topTxtSize, 0)
        mTopTxtColor = ta.getColor(R.styleable.NodeProgressBar_topTxtColor, Color.TRANSPARENT)
        mTopTxtGap = ta.getDimensionPixelSize(R.styleable.NodeProgressBar_topTxtGap, 0)
        mTopTxtStyle = ta.getInteger(R.styleable.NodeProgressBar_topTxtStyle, TxtStyle.BOLD)
        mBottomTxtEnable = ta.getBoolean(R.styleable.NodeProgressBar_bottomTxtEnable, false)
        mBottomTxtSize = ta.getDimensionPixelSize(R.styleable.NodeProgressBar_bottomTxtSize, 0)
        mBottomTxtColor = ta.getColor(R.styleable.NodeProgressBar_bottomTxtColor, Color.TRANSPARENT)
        mBottomTxtGap = ta.getDimensionPixelSize(R.styleable.NodeProgressBar_bottomTxtGap, 0)
        mBottomTxtStyle = ta.getInteger(R.styleable.NodeProgressBar_bottomTxtStyle, TxtStyle.COMMON)
        mBottomWarnTxtColor =
            ta.getColor(R.styleable.NodeProgressBar_bottomWarnTxtColor, Color.TRANSPARENT)
        mBottomWarnTxtStyle =
            ta.getInteger(R.styleable.NodeProgressBar_bottomWarnTxtStyle, TxtStyle.COMMON)
        mLineWidth = ta.getDimensionPixelSize(R.styleable.NodeProgressBar_lineWidth, 0)
        mReachedLineColor =
            ta.getColor(R.styleable.NodeProgressBar_reachedLineColor, Color.TRANSPARENT)
        mUnreachedLineColor =
            ta.getColor(R.styleable.NodeProgressBar_unreachedLineColor, Color.TRANSPARENT)
        mRegionWidth = ta.getDimensionPixelSize(R.styleable.NodeProgressBar_regionWidth, 0)
        ta.recycle()
        configBitmaps(context)
        configPaints()
    }

    /**
     * 配置画笔
     */
    private fun configPaints() {
        // 上方文字画笔属性设置
        configTopTxtPaint()
        // 下方文字画笔属性设置
        configBottomTxtPaint()
        // 下方提示文字画笔属性设置
        configBottomWarnTxtPaint()
        // 节点画笔属性设置
        configNodePaint()
        // 未到达连接线画笔属性设置
        configUnreachedLinePaint()
        // 已到达连接线画笔属性设置
        configReachedLinePaint()
    }

    /**
     * 已到达连接线画笔属性设置
     */
    private fun configReachedLinePaint() {
        mPaintReachedLine = Paint()
        mPaintReachedLine!!.color = mReachedLineColor
        mPaintReachedLine!!.strokeWidth = mLineWidth.toFloat()
        mPaintReachedLine!!.style = Paint.Style.FILL
        mPaintReachedLine!!.isAntiAlias = true
    }

    /**
     * 未到达连接线画笔属性设置
     */
    private fun configUnreachedLinePaint() {
        mPaintUnreachedLine = Paint()
        mPaintUnreachedLine!!.color = mUnreachedLineColor
        mPaintUnreachedLine!!.strokeWidth = mLineWidth.toFloat()
        mPaintUnreachedLine!!.style = Paint.Style.FILL
        mPaintUnreachedLine!!.isAntiAlias = true
    }

    /**
     * 节点画笔属性设置
     */
    private fun configNodePaint() {
        mPaintNode = Paint()
        mPaintNode!!.isAntiAlias = true
    }

    /**
     * 下方提示文字画笔属性设置
     */
    private fun configBottomWarnTxtPaint() {
        mPaintBottomWarnTxt = Paint()
        mPaintBottomWarnTxt!!.textSize = mBottomTxtSize.toFloat()
        mPaintBottomWarnTxt!!.color = mBottomWarnTxtColor
        mPaintBottomWarnTxt!!.textAlign = Paint.Align.CENTER
        mPaintBottomWarnTxt!!.isAntiAlias = true
        if (TxtStyle.COMMON == mBottomWarnTxtStyle) {
            mPaintBottomWarnTxt!!.typeface = Typeface.DEFAULT
        } else if (TxtStyle.BOLD == mBottomWarnTxtStyle) {
            mPaintBottomWarnTxt!!.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
        } else if (TxtStyle.ITALIC == mBottomWarnTxtStyle) {
            mPaintBottomWarnTxt!!.typeface = Typeface.defaultFromStyle(Typeface.ITALIC)
        }
    }

    /**
     * 下方文字画笔属性设置
     */
    private fun configBottomTxtPaint() {
        mPaintBottomTxt = Paint()
        mPaintBottomTxt!!.textSize = mBottomTxtSize.toFloat()
        mPaintBottomTxt!!.color = mBottomTxtColor
        mPaintBottomTxt!!.textAlign = Paint.Align.CENTER
        mPaintBottomTxt!!.isAntiAlias = true
        if (TxtStyle.COMMON == mBottomTxtStyle) {
            mPaintBottomTxt!!.typeface = Typeface.DEFAULT
        } else if (TxtStyle.BOLD == mBottomTxtStyle) {
            mPaintBottomTxt!!.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
        } else if (TxtStyle.ITALIC == mBottomTxtStyle) {
            mPaintBottomTxt!!.typeface = Typeface.defaultFromStyle(Typeface.ITALIC)
        }
    }

    /**
     * 上方文字画笔属性设置
     */
    private fun configTopTxtPaint() {
        mPaintTopTxt = Paint()
        mPaintTopTxt!!.textSize = mTopTxtSize.toFloat()
        mPaintTopTxt!!.color = mTopTxtColor
        mPaintTopTxt!!.textAlign = Paint.Align.CENTER
        mPaintTopTxt!!.isAntiAlias = true
        if (TxtStyle.COMMON == mTopTxtStyle) {
            mPaintTopTxt!!.typeface = Typeface.DEFAULT
        } else if (TxtStyle.BOLD == mTopTxtStyle) {
            mPaintTopTxt!!.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
        } else if (TxtStyle.ITALIC == mTopTxtStyle) {
            mPaintTopTxt!!.typeface = Typeface.defaultFromStyle(Typeface.ITALIC)
        }
    }

    /**
     * 配置bitmap
     *
     * @param context
     */
    private fun configBitmaps(context: Context) {
        val resources = context.resources
        mNodeUnreachedBitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_loc_white_icon)
        mNodeReachedBitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_loc_pro_icon)
        mNodeFailedBitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_loc_white_icon)
        mNodeFinishedBitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_loc_white_icon)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        mWidth = MeasureSpec.getSize(widthMeasureSpec)
        mHeight = MeasureSpec.getSize(heightMeasureSpec)
        //        if (mTopTxtEnable && mBottomTxtEnable) {// 上下文字都展示
//            mHeight = mTopTxtSize + mTopTxtGap + mNodeHeight + mBottomTxtGap + mBottomTxtSize;
//        } else if (mTopTxtEnable) {// 仅上方文字展示
//            mHeight = mTopTxtSize + mTopTxtGap + mNodeHeight;
//        } else if (mBottomTxtEnable) {// 仅下方文字展示
//            mHeight = mNodeHeight + mBottomTxtGap + mBottomTxtSize;
//        } else {// 不展示上下文字
//            mHeight = mNodeHeight;
//        }
        // 上线各加1dp的余量，防止个别情况下展示不全
        setMeasuredDimension(mWidth, mHeight)
    }

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (mNodeList.size != mNodeCount || mNodeList.isEmpty()) {
            return
        }

        // 初始化位置列表
        initLocationLists()

        // 测量坐标
        measureLocations()

        // 绘制上方文字
        if (mTopTxtEnable) {
            for (i in 0 until mNodeCount) {
                val node = mNodeList[i]
                if (TextUtils.isEmpty(node.topTxt)) {
                    continue
                }
                val metrics = mPaintTopTxt!!.fontMetrics
                val x = mTopTxtLocationList!![i].x
                val y = (mTopTxtLocationList!![i].y + Math.abs(
                    mPaintTopTxt!!.ascent() + mPaintTopTxt!!.descent() / 2
                )).toInt()
                canvas.drawText(node.topTxt!!, x.toFloat(), y.toFloat(), mPaintTopTxt!!)
            }
        }

        // 绘制连线
        for (i in 0 until mNodeCount) {
            val node = mNodeList[i]
            if (i == mNodeCount - 1) {
                break
            }
            val x1 = mNodeLocationList!![i].x
            val y1 = mNodeLocationList!![i].y
            val x2 = mNodeLocationList!![i + 1].x
            val y2 = mNodeLocationList!![i + 1].y
            if (Node.LineState.UNREACHED == node.nodeAfterLineState) {
                canvas.drawLine(
                    x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat(),
                    mPaintUnreachedLine!!
                )
            } else if (Node.LineState.REACHED == node.nodeAfterLineState) {
                canvas.drawLine(
                    x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat(),
                    mPaintReachedLine!!
                )
            } else {
                canvas.drawLine(
                    x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat(),
                    mPaintUnreachedLine!!
                )
            }
        }

        // 绘制节点
        for (i in 0 until mNodeCount) {
            val node = mNodeList[i]
            val x = mNodeLocationList!![i].x
            val y = mNodeLocationList!![i].y
            if (Node.NodeState.UNREACHED == node.nodeState) {
                val rect = Rect(0, 0, mNodeUnreachedBitmap!!.width, mNodeUnreachedBitmap!!.height)
                val rectF = RectF(
                    x - mNodeRatio * mNodeWidth / 2,
                    y - mNodeRatio * mNodeHeight / 2,
                    x + mNodeRatio * mNodeWidth / 2,
                    y + mNodeRatio * mNodeHeight / 2
                )
                canvas.drawBitmap(mNodeUnreachedBitmap!!, rect, rectF, mPaintNode)
            } else if (Node.NodeState.REACHED == node.nodeState) {
                val rect = Rect(0, 0, mNodeUnreachedBitmap!!.width, mNodeUnreachedBitmap!!.height)
                val rectF = RectF(
                    x - mNodeRatio * mNodeWidth / 2,
                    y - mNodeRatio * mNodeHeight / 2,
                    x + mNodeRatio * mNodeWidth / 2,
                    y + mNodeRatio * mNodeHeight / 2
                )
                canvas.drawBitmap(mNodeReachedBitmap!!, rect, rectF, mPaintNode)
            } else if (Node.NodeState.FAILED == node.nodeState) {
                val rect = Rect(0, 0, mNodeUnreachedBitmap!!.width, mNodeUnreachedBitmap!!.height)
                val rectF = RectF(
                    x - 1.0f * mNodeWidth / 2,
                    y - 1.0f * mNodeHeight / 2,
                    x + 1.0f * mNodeWidth / 2,
                    (y + mNodeHeight / 2).toFloat()
                )
                canvas.drawBitmap(mNodeFailedBitmap!!, rect, rectF, mPaintNode)
            } else if (Node.NodeState.FINISHED == node.nodeState) {
                val rect = Rect(0, 0, mNodeUnreachedBitmap!!.width, mNodeUnreachedBitmap!!.height)
                val rectF = RectF(
                    x - 1.0f * mNodeWidth / 2,
                    y - 1.0f * mNodeHeight / 2,
                    x + 1.0f * mNodeWidth / 2,
                    y + 1.0f * mNodeHeight / 2
                )
                canvas.drawBitmap(mNodeFinishedBitmap!!, rect, rectF, mPaintNode)
            }
        }

        // 绘制下方文字
        if (mBottomTxtEnable) {
            for (i in 0 until mNodeCount) {
                val node = mNodeList[i]
                if (TextUtils.isEmpty(node.bottomTxt)) {
                    continue
                }
                val x = mBottomTxtLocationList!![i].x
                val y = (mBottomTxtLocationList!![i].y + Math.abs(
                    mPaintBottomTxt!!.ascent() + mPaintBottomTxt!!.descent() / 2
                )).toInt()
                if (Node.NodeState.FAILED != node.nodeState) {
                    canvas.drawText(
                        node.bottomTxt!!, x.toFloat(), y.toFloat(),
                        mPaintBottomTxt!!
                    )
                } else {
                    canvas.drawText(
                        node.bottomTxt!!, x.toFloat(), y.toFloat(),
                        mPaintBottomWarnTxt!!
                    )
                }
            }
        }
    }

    private fun initLocationLists() {
        if (mTopTxtLocationList != null) {
            mTopTxtLocationList!!.clear()
        } else {
            mTopTxtLocationList = ArrayList()
        }
        if (mNodeLocationList != null) {
            mNodeLocationList!!.clear()
        } else {
            mNodeLocationList = ArrayList()
        }
        if (mBottomTxtLocationList != null) {
            mBottomTxtLocationList!!.clear()
        } else {
            mBottomTxtLocationList = ArrayList()
        }
    }

    /**
     * 测量元素的中心坐标
     */
    private fun measureLocations() {
        if (mNodeCount == 1) {
            // 上方文字的中心坐标
            if (mTopTxtEnable) {
                val topTxtLoc = Location()
                topTxtLoc.x = mWidth / 2
                topTxtLoc.y = mTopTxtSize / 2
                mTopTxtLocationList!!.add(topTxtLoc)
            }

            // 节点的中心坐标
            if (mTopTxtEnable) {
                val nodeLoc = Location()
                nodeLoc.x = mWidth / 2
                nodeLoc.y = mTopTxtSize + mTopTxtGap + mNodeHeight / 2
                mNodeLocationList!!.add(nodeLoc)
            } else {
                val nodeLoc = Location()
                nodeLoc.x = mWidth / 2
                nodeLoc.y = mNodeHeight / 2
                mNodeLocationList!!.add(nodeLoc)
            }

            // 下方文字的中心坐标
            if (mTopTxtEnable && mBottomTxtEnable) {
                val bottomTxtLoc = Location()
                bottomTxtLoc.x = mWidth / 2
                bottomTxtLoc.y =
                    mTopTxtSize + mTopTxtGap + mNodeHeight + mBottomTxtGap + mBottomTxtSize / 2
                mBottomTxtLocationList!!.add(bottomTxtLoc)
            } else if (mBottomTxtEnable) {
                val bottomTxtLoc = Location()
                bottomTxtLoc.x = mWidth / 2
                bottomTxtLoc.y = mNodeHeight + mBottomTxtGap + mBottomTxtSize / 2
                mBottomTxtLocationList!!.add(bottomTxtLoc)
            }
            return
        }
        val space = (mWidth - mRegionWidth * mNodeCount) / (mNodeCount - 1)
        for (i in 0 until mNodeCount) {
            // 上方文字的中心坐标
            if (mTopTxtEnable) {
                val topTxtLoc = Location()
                topTxtLoc.x = mRegionWidth / 2 + i * space + i * mRegionWidth
                topTxtLoc.y = mTopTxtSize / 2
                mTopTxtLocationList!!.add(topTxtLoc)
            }

            // 节点的中心坐标
            if (mTopTxtEnable) {
                val nodeLoc = Location()
                nodeLoc.x = mRegionWidth / 2 + i * space + i * mRegionWidth
                nodeLoc.y = mTopTxtSize + mTopTxtGap + mNodeHeight / 2
                mNodeLocationList!!.add(nodeLoc)
            } else {
                val nodeLoc = Location()
                nodeLoc.x = mRegionWidth / 2 + i * space + i * mRegionWidth
                nodeLoc.y = mNodeHeight / 2
                mNodeLocationList!!.add(nodeLoc)
            }

            // 下方文字的中心坐标
            if (mTopTxtEnable && mBottomTxtEnable) {
                val bottomTxtLoc = Location()
                bottomTxtLoc.x = mRegionWidth / 2 + i * space + i * mRegionWidth
                bottomTxtLoc.y =
                    mTopTxtSize + mTopTxtGap + mNodeHeight + mBottomTxtGap + mBottomTxtSize / 2
                mBottomTxtLocationList!!.add(bottomTxtLoc)
            } else if (mBottomTxtEnable) {
                val bottomTxtLoc = Location()
                bottomTxtLoc.x = mRegionWidth / 2 + i * space + i * mRegionWidth
                bottomTxtLoc.y = mNodeHeight + mBottomTxtGap + mBottomTxtSize / 2
                mBottomTxtLocationList!!.add(bottomTxtLoc)
            }
        }
    }

    /**
     * 上方文字是否生效
     *
     * @param mTopTxtEnable
     */
    fun setTopTxtEnable(mTopTxtEnable: Boolean) {
        this.mTopTxtEnable = mTopTxtEnable
        invalidate()
    }

    /**
     * 下方文字是否生效
     *
     * @param mBottomTxtEnable
     */
    fun setBottomTxtEnable(mBottomTxtEnable: Boolean) {
        this.mBottomTxtEnable = mBottomTxtEnable
        invalidate()
    }

    /**
     * 设置节点信息
     *
     * @param mNodeList
     */
    fun setNodeList(mNodeList: List<Node>) {
        this.mNodeList = mNodeList
        mNodeCount = mNodeList.size
        invalidate()
    }

    /**
     * 中心坐标
     */
    private class Location {
        var x = 0
        var y = 0
    }

    /**
     * 节点对象
     */
    class Node {
        interface NodeState {
            companion object {
                const val UNREACHED = 1
                const val REACHED = 2
                const val FINISHED = 3
                const val FAILED = 4
            }
        }

        interface LineState {
            companion object {
                const val REACHED = 0
                const val UNREACHED = 1
            }
        }

        // 节点上方文字
        var topTxt: String? = null

        // 节点下方文字
        var bottomTxt: String? = null

        // 节点状态
        var nodeState = 0

        // 节点后连线状态
        var nodeAfterLineState = 0
    }

    /**
     * 字体
     */
    interface TxtStyle {
        companion object {
            const val COMMON = 0
            const val BOLD = 1
            const val ITALIC = 2
        }
    }
}
